#pragma once

#include <iostream>
#include <vector>
#include <string>
#include <semaphore.h>

// 单生产，单消费
// 多生产，多消费
// “321”
// 3：三种关系
// a. 生产和消费互斥和同步
// b. 生产者之间？
// c. 消费者之间？
// 解决方案：加锁
// 1. 需要几把锁 2把 生产者一把 消费者一把
// 2. 如何加锁


template <typename T>
class RingQueue
{
private:
    void P(sem_t &sem)
    {
        sem_wait(&sem);
    }
    void V(sem_t &sem)
    {
        sem_post(&sem);
    }
    void Lock(pthread_mutex_t& mutex)
    {
        pthread_mutex_lock(&mutex);
    }
    void Unlock(pthread_mutex_t& mutex)
    {
        pthread_mutex_unlock(&mutex);
    }

public:
    RingQueue(int cap) : _cap(cap), _ring_queue(cap), _productor_step(0), _consumer_step(0)
    {
        // 初始化信号量
        sem_init(&_room_sem, 0, _cap);
        sem_init(&_data_sem, 0, 0);

        // 初始化锁
        pthread_mutex_init(&_productor_mutex, nullptr);
        pthread_mutex_init(&_consumer_mutex, nullptr);
    }

    void Enqueue(const T &in)
    {
        // 抢锁之前先申请信号量，就像先买电影票再去排队检票进场一样
        // 竞争锁需要等待，在等之前就先把信号量散出去，申请好
        P(_room_sem); 

        Lock(_productor_mutex); // 让多个生产者竞争锁，竞争成功才可以进行生产
        // 生产行为
        // 获取到了 信号量 就说明一定有空间
        _ring_queue[_productor_step++] = in; // 生产
        _productor_step %= _cap;
        V(_data_sem);

        Unlock(_productor_mutex);
    }

    void Pop(T *out) 
    {
        P(_data_sem);

        Lock(_consumer_mutex); // 让多个消费者竞争锁，竞争成功才可以进行消费
        
        // 消费行为
        *out = _ring_queue[_consumer_step++];
        _consumer_step %= _cap;
        V(_room_sem);

        Unlock(_consumer_mutex);
    }

    ~RingQueue()
    {
        // 销毁信号量
        sem_destroy(&_room_sem);
        sem_destroy(&_data_sem);
    }

private:
    // 1、环形队列
    std::vector<T> _ring_queue;
    int _cap; // 环形队列的容量上线

    // 2、生产和消费的下标
    int _consumer_step;
    int _productor_step;

    // 3、定义信号量
    sem_t _room_sem;
    sem_t _data_sem;

    // 4、定义锁，维护多生产多消费之间的互斥关系
    pthread_mutex_t _productor_mutex;
    pthread_mutex_t _consumer_mutex;
};


/* --- 以下代码只能实现 单生产单消费，如果变为多生产多消费会有线程安全问题，必须得加锁 --- */
// template <typename T>
// class RingQueue
// {
// private:
//     void P(sem_t &sem)
//     {
//         sem_wait(&sem);
//     }

//     void V(sem_t &sem)
//     {
//         sem_post(&sem);
//     }

// public:
//     RingQueue(int cap) : _cap(cap), _ring_queue(cap), _productor_step(0), _consumer_step(0)
//     {
//         // 初始化信号量
//         sem_init(&_room_sem, 0, _cap);
//         sem_init(&_data_sem, 0, 0);
//     }

//     void Enqueue(const T &in)
//     {
//         // 生产行为
//         P(_room_sem);

//         // 获取到了 信号量 就说明一定有空间
//         _ring_queue[_productor_step++] = in; // 生产
//         _productor_step %= _cap;
//         V(_data_sem);
//     }

//     void Pop(T *out) 
//     {
//         // Lock(_consumer_step);
//         // 消费行为
//         P(_data_sem);
//         *out = _ring_queue[_consumer_step++];
//         _consumer_step %= _cap;
//         V(_room_sem);
//     }

//     ~RingQueue()
//     {
//         // 销毁信号量
//         sem_destroy(&_room_sem);
//         sem_destroy(&_data_sem);
//     }

// private:
//     // 1、环形队列
//     std::vector<T> _ring_queue;
//     int _cap; // 环形队列的容量上线

//     // 生产和消费的下标
//     int _consumer_step;
//     int _productor_step;

//     // 3、定义信号量
//     sem_t _room_sem;
//     sem_t _data_sem;
// };